//! 物理内存的分配释放接口

use crate::{
    cache::SMP_CACHE_BYTES,
    vm::{addr::paddr::Paddr, pgtable::page::PAGE_SIZE},
};

use super::{common::memblock_remove_range, Memblock, MemblockFlags, MEMBLOCK_ALLOC_ACCESSIBLE};

fn memblock_find_range_bottom_up(
    start: Paddr,
    end: Paddr,
    size: usize,
    align: usize,
    flags: MemblockFlags,
    memblock: &Memblock,
) -> Paddr {
    for (mut this_start, mut this_end) in memblock.iter_free(flags) {
        this_start = this_start.clamp(start, end);
        this_end = this_end.clamp(start, end);

        let cand = this_start.to_value().next_multiple_of(align);
        if cand < this_end.to_value() && this_end.to_value() - cand >= size {
            return Paddr::from(cand);
        }
    }
    Paddr::from(0)
}

fn memblock_find_range_top_down(
    start: Paddr,
    end: Paddr,
    size: usize,
    align: usize,
    flags: MemblockFlags,
    memblock: &Memblock,
) -> Paddr {
    for (mut this_start, mut this_end) in memblock.iter_free_rev(flags) {
        this_start = this_start.clamp(start, end);
        this_end = this_end.clamp(start, end);

        if this_end.to_value() < size {
            continue;
        }
        let cand = (this_end - size).to_value().next_multiple_of(align) - align;
        if cand >= this_start.to_value() {
            return Paddr::from(cand);
        }
    }
    Paddr::from(0)
}

fn __memblock_find_in_range(
    size: usize,
    align: usize,
    mut start: Paddr,
    mut end: Paddr,
    flags: MemblockFlags,
    memblock: &Memblock,
) -> Paddr {
    if end.to_value() == MEMBLOCK_ALLOC_ACCESSIBLE {
        end = memblock.current_limit;
    }

    start = start.max(Paddr::from(PAGE_SIZE));
    end = end.max(start);

    if memblock.is_bottom_up() && end > memblock.bottom_limit() {
        let bottom_up_start = start.max(memblock.bottom_limit());
        let ret = memblock_find_range_bottom_up(bottom_up_start, end, size, align, flags, memblock);
        if ret.to_value() != 0 {
            return ret;
        }
    }
    memblock_find_range_top_down(start, end, size, align, flags, memblock)
}

fn memblock_alloc_internal(
    size: usize,
    mut align: usize,
    mut min_addr: Paddr,
    mut max_addr: Paddr,
    memblock: &mut Memblock,
) -> Paddr {
    let flags = MemblockFlags::MEMBLOCK_NONE;
    if align == 0 {
        align = SMP_CACHE_BYTES;
    }

    if max_addr > memblock.current_limit() {
        max_addr = memblock.current_limit();
    }

    loop {
        let alloc = __memblock_find_in_range(size, align, min_addr, max_addr, flags, memblock);
        if alloc.to_value() != 0 && memblock.reserve(alloc, size) == Ok(()) {
            return alloc;
        }
        if min_addr.to_value() != 0 {
            min_addr = Paddr::from(0);
            continue;
        }
        return Paddr::from(0);
    }
}

impl Memblock {
    /// 尝试从指定范围进行内存分配
    ///
    /// 默认从 MemBlockFlags::MEMBLOCK_NONE 进行分配
    ///
    /// 返回值:
    /// * 成功返回分配的物理地址
    /// * 失败返回 0
    pub fn alloc_try(
        &mut self,
        size: usize,
        align: usize,
        min_addr: Paddr,
        max_addr: Paddr,
    ) -> Result<Paddr, ()> {
        let ret = memblock_alloc_internal(size, align, min_addr, max_addr, self);
        if ret.to_value() == 0 {
            return Err(());
        }
        Ok(ret)
    }

    /// 从任意范围进行内存分配
    ///
    /// 默认从 MemBlockFlags::MEMBLOCK_NONE 进行分配
    ///
    /// 返回值:
    /// * 成功返回分配的物理地址
    /// * 失败返回 0
    pub fn alloc(&mut self, size: usize, align: usize) -> Result<Paddr, ()> {
        self.alloc_try(
            size,
            align,
            Paddr::from(0),
            Paddr::from(MEMBLOCK_ALLOC_ACCESSIBLE),
        )
    }

    /// 从指定的最大地址范围进行内存分配
    ///
    /// 默认从 MemBlockFlags::MEMBLOCK_NONE 进行分配
    ///
    /// 返回值:
    /// * 成功返回分配的物理地址
    /// * 失败返回 0
    pub fn alloc_from(&mut self, size: usize, align: usize, min_addr: Paddr) -> Result<Paddr, ()> {
        self.alloc_try(
            size,
            align,
            min_addr,
            Paddr::from(MEMBLOCK_ALLOC_ACCESSIBLE),
        )
    }

    /// 从指定的范围和 flags 中进行内存分配
    ///
    /// 返回值:
    /// * 成功返回分配的物理地址
    /// * 失败返回 0
    pub fn alloc_range(
        &mut self,
        size: usize,
        mut align: usize,
        start: Paddr,
        end: Paddr,
        flags: MemblockFlags,
    ) -> Result<Paddr, ()> {
        if align == 0 {
            align = SMP_CACHE_BYTES;
        }
        let found = __memblock_find_in_range(size, align, start, end, flags, self);
        if found.to_value() != 0 && self.reserve(found, size) == Ok(()) {
            return Ok(found);
        }
        Err(())
    }

    /// 将分配的内存释放回 MemBlock 中
    ///
    /// 返回值:
    /// * OK(()) - 成功
    /// * Err(()) - 失败
    pub fn free(&mut self, base: Paddr, size: usize) -> Result<(), ()> {
        memblock_remove_range(&mut self.reserved, base, size)
    }
}
